#!/bin/bash

# Copyright 2023 The LUCI Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# sample usage and meaning
#
# ./test-fft -i   ~/cr     -p      9
#               infra-dir      patchset-#

# Defensively set the CDPATH to something safe before doing anything.
export CDPATH=

# This script owns the branch below in every git repo ever. This should be safe
# since it contains a UUID.
readonly MY_INFRA_BRANCH='ftt-test-branch-149cb550-9322-46a2-a27d-06c05781102d'
export MY_INFRA_BRANCH
# We use the devel branch in the LUCI repo to make it easier to patch the
# commit series and upload a new chain.
readonly MY_DEVEL_BRANCH='ftt-devel-branch-91e1de00-c43a-4d07-b6fe-ed05322a465b'
export MY_DEVEL_BRANCH

# die writes a message to stderr and then exits abnormally.
die() {
  printf '%s\n' "$@" 1>&2
  exit 1
}

# checkdep checks for a dependency and dies if it isn't available.
checkdep() {
  which -- "$1" 1>/dev/null 2>/dev/null || die 'missing dependency: '"$1"
}

# backup_self backs up the currently-executing script to ~/test-ftt.
# We do this for convenience.
#
# The script test-ftt is itself checked into the LUCI git repo.
# It also manipulates the git state, which can cause changes to the script
# to be removed from the working directory. We copy ourselves to the home
# directory in order to make it easier to tweak something and then immediately
# run the script again.
backup_self() {
  cp -- "${HOME}/test-ftt" "${HOME}/test-ftt.BAK" 1>/dev/null 2>/dev/null
  if cmp --silent -- "${selfpath}" "${HOME}"/test-ftt; then
    return
  fi
  cp -- "${selfpath}" "${HOME}"/test-ftt || die 'failed to back up self'
}

# must_check_git_clean checks that the git repo pointed to by $1 is clean.
# We die informatively if it is not clean.
#
# Submodules being dirty or pointing to the wrong commit is okay here. We are only
# concerend about preventing the user from losing work.
must_check_git_clean() {
  [[ -z "$(git -C "$1" status --porcelain --ignore-submodules=all)" ]] || die "directory '$1' is not clean"
}

# must_checkout_patchset_in_luci_dir checks out the patchset specified by the user in the luci directory.
# This function is only safe to call if we have already checked that the directory is clear.
must_checkout_patchset_in_luci_dir() {
  # (1/5) Clear the local version of the dev branch so we have somewhere to work. We don't care whether the branch exists or not.
  git -C "$luci_dir" branch -D "$MY_DEVEL_BRANCH" 1>/dev/null 2>/dev/null
  # (2/5) Fetch the user-specified patchset. Don't check whether it's the latest or not because I don't know how to do that.
  git -C "$luci_dir" fetch https://chromium.googlesource.com/infra/luci/luci-go refs/changes/51/4917851/"$patchset" || die 'failed to fetch patchset'
  # (3/5) Move the working directory to point at the thing that we just fetched.
  git -C "$luci_dir" checkout FETCH_HEAD || die 'failed to check out fetch head after fetching patch. Wowzers.'
  # (4/5) Drop a branch to save our current location. Use the branch name that we freed in step 1.
  git -C "$luci_dir" checkout -b "$MY_DEVEL_BRANCH" || die 'failed to drop development branch for later devleopment'
  # (5/5) Set the tracking branch of the branch that we just dropped to the upstream so that we can run 'git cl upload' later.
  git -C "$luci_dir" branch -u "origin/main" || die 'failed to set the upstream branch of the development branch correctly'
}

# main takes the input args and runs the tests.
main() {
  local -r usage='test-ftt [-h] [-i infra-dir] [-p patchset-number]

  This script produces the branch "ftt-test-branch-149cb550-9322-46a2-a27d-06c05781102d"
  in the infra tree. This branch is temporary.

  This script produces the branch "ftt-devel-branch-149cb550-9322-46a2-a27d-06c05781102d"
  in the LUCI tree. This branch can be modified and then re-uploaded.
  '

  while getopts ':hi:p:' opt; do
    case "${opt}" in
      h) printf '%s' "${usage}"; exit 0;;
      i) local infra_dir="${OPTARG}";;
      p) local patchset="${OPTARG}";;
      :)
         1>&2 printf '%s' "${usage}"; exit 1;;
      *)
         1>&2 printf '%s' "${usage}"; exit 1;;
    esac
  done

  checkdep 'realpath'
  checkdep 'git'
  checkdep 'mktemp'
  checkdep 'cp'
  checkdep 'unlink'

  [[ -n ${infra_dir} ]] || die "no directory given"
  [[ -e ${infra_dir} ]] || die "path \`${infra_dir}' does not exist"
  [[ -d ${infra_dir} ]] || die "path \`${infra_dir}' is not directory"

  [[ -n "${patchset}" ]] || die 'no patchset given'

  local -r selfpath="$(realpath -- "$0")"

  local -r infra_dir="$(realpath -- "${infra_dir}")" || die "failed to get realpath to \`$1'"

  # cd to a defensive dir so we catch inadvertent uses of relative paths rather
  # than computed absolute paths.
  local -r defensive_dir="$(mktemp -d)" || die 'failed to make defensive dir'
  cd -- "${defensive_dir}" || die 'failed to cd to defensive dir'

  [[ -f ${infra_dir}/.gclient ]] || die 'failed heuristic: no gclient file: is the file path correct?'

  local -r luci_dir="${infra_dir}"/infra/go/src/go.chromium.org/luci

  [[ -d $luci_dir ]] || die "luci directory \`${luci_dir}' does not exist or is not directory"
  [[ -f $luci_dir/AUTHORS ]] || die "failed heuristic: derived directory \`${luci_dir}' has no AUTHORS file"

  local -r infra_go_root="${infra_dir}/infra/go/src/infra"
  [[ -d "${infra_dir}" ]] || die "infra go root directory ${infra_go_root} does not exist"
  [[ -d "${infra_go_root}/unifiedfleet" ]] || die "failed heuristic: derived directory '${infra_go_root}/unifiedfleet' does not exist."

  must_check_git_clean "${infra_go_root}"
  must_check_git_clean "${luci_dir}"

  # Back ourselves up, after this point all subsequent commands can change the git state.
  backup_self

  must_checkout_patchset_in_luci_dir

  die 'not yet implemented'
}

main "$@"
